﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Emil.GMP;

namespace BYU_ECC
{
    class Curve
    {
        #region Curve Members and Properties
        private static BigInt mod;
		public static BigInt Mod {
            get 
            {
                CheckGenerated();
                return mod;
            }
        }

		private static BigInt a;
		public static BigInt A
        {
            get
            {
                CheckGenerated();
                return a;
            }
        }

		private static BigInt b;
		public static BigInt B
        {
            get
            {
                CheckGenerated();
                return b;
            }
        }
        #endregion

        #region Singleton Instance Functions
        private static Curve instance;
        public Curve Instance()
        {
            CheckGenerated();
            return instance;
        }
		public static void GenerateCurve(BigInt a, BigInt b, BigInt mod)
        {
            instance = new Curve(a, b, mod);
        }
		private Curve(BigInt _a, BigInt _b, BigInt _mod)
        {
            a = _a;
            b = _b;
            mod = _mod;
        }
        #endregion

        private static void CheckGenerated()
        {
            if (instance == null)
                throw new CurveNotGeneratedException();
        }

        public static bool IsValidPoint(Point p)
        {
            if (p == Point.Infinity)
                return true;
            else 
                return IsValidPoint(p.x, p.y); 
        }

		public static bool IsValidPoint(BigInt x, BigInt y)
        {
            CheckGenerated();
            
            //This is the original function - essentially ignores mod.
            //return Math.Power(y, 2) == (Math.Power(x, 3) + a * x + b);

            //This is the mod-based function. I don't know which one to use.
            //Is on curve if (y^2) mod n == (x^3 + ax + b) mod n
            return y.PowerMod(2, mod) == (x.Power(3) + (a * x) + b).Mod(mod);

        }

		public static Point MakePoint(BigInt x, BigInt y)
        {
            CheckGenerated();
            if (!IsValidPoint(x, y))
                throw new PointNotOnCurveException(x,y);
            return new Point(x, y);
        }

        public static BigInt getYfromX(BigInt x)
        {
			return (x.Power(3) + a * x + b).Sqrt();
        }

    }

    class CurveNotGeneratedException : Exception { }

    class PointNotOnCurveException : Exception
    {
        string stringInfo;

        public PointNotOnCurveException() : base() { }
        public PointNotOnCurveException(string message) : base(message) { }
        public PointNotOnCurveException(string message, Exception inner) : base(message, inner) { }

		public PointNotOnCurveException(BigInt x, BigInt y) 
        {
            stringInfo = String.Format("The point ({0},{1}) does not lie on the curve.", x, y);
        }

        public PointNotOnCurveException(Point p)
        {
            stringInfo = String.Format("The point ({0},{1}) does not lie on the curve.", p.x, p.y);
        }

        public override string Message
        {
            get
            {
                string message = base.Message;
                if (stringInfo != null)
                {
                    message += Environment.NewLine + stringInfo;
                }
                return message;
            }
        }

        public override string ToString()
        {
            return Message;
        }
    }
}
